//
//  ConsoleViewController.m
//  FreeSWITCH_Console
//
//  Created by Christopher Danielson on 4/6/09.
//  Copyright 2009 MaxPowerSoft, LLC. All rights reserved.
//

#import "ConsoleViewController.h"
#import "AsyncSocket.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
#import <SystemConfiguration/SCNetworkReachability.h>

#define FS_AUTH_TAG		10
#define FS_API_TAG		20
#define FS_BGAPI_TAG	30
#define FS_EVENT_TAG	40
#define FS_LOG_TAG		50

#define GENERIC_SOCK_READ_TIMEOUT	120

@implementation ConsoleViewController

@synthesize as;
@synthesize _delegate;
@synthesize consoleTextView;
@synthesize consoleInputField;	
@synthesize userSetting;
@synthesize fsCmd;
@synthesize timer;


// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
		[self becomeFirstResponder];
		[self.consoleTextView becomeFirstResponder];
    }
    return self;
}


/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/

#pragma mark FS Data Helpers
-(void) writeToConsole:(NSString *)str {
	CGPoint p = [consoleTextView contentOffset];
	consoleTextView.text = [NSString stringWithFormat:@"%@\n%@", consoleTextView.text, str];
	[consoleTextView setContentOffset:p animated:NO];
	[consoleTextView scrollRangeToVisible:NSMakeRange([consoleTextView.text length], 0)];
}

-(void) handleFSConnection {
	NSLog(@"handleFSConnection\n");
	if (![as isConnected]) {
		if ([as connectedHost] != nil) { //cleanup if we've already been established
			[as disconnectAfterReadingAndWriting];
			NSLog(@"Disconnecting socket and attempting reconnection.\n");
		}
		NSString *authStr = [[NSString alloc] initWithFormat:@"Authenticating with %@:%d\n", userSetting.host, userSetting.port];
		[self writeToConsole:authStr];
		[authStr release];
		NSError *err;
		[as connectToHost:userSetting.host onPort:userSetting.port error:&err];
		NSString * str = [[NSString alloc] initWithFormat:@"auth %@\r\n\r\n", userSetting.password];
		NSData *authRequest = [str dataUsingEncoding:NSASCIIStringEncoding];
		[as writeData:authRequest withTimeout:10 tag:FS_AUTH_TAG];
		[str release];
		[as readDataWithTimeout:GENERIC_SOCK_READ_TIMEOUT tag:FS_AUTH_TAG];
	}
}

-(void) sendFSCmdData {
	if (fsCmd != nil) {
		[self handleFSConnection];
		//NSString *cmd = @"api sofia status\r\n\r\n";
		/*
		 - 'console' for api response messages
		 - 'log'     for freeswitch log output (simply send the log level you want 
		 to start seeing events eg: 'log all')
		 - 'event'   for freeswitch event output (must subscribe in plain format
		 eg: 'event plain all')		 
		*/
		NSString *cmd = nil;
		//if ($input =~ /^log|^event/)
		NSInteger readType = 0;
		
		NSRange rangeLog = [fsCmd rangeOfString: @"log"];
		NSRange rangeEvent = [fsCmd rangeOfString: @"event"];
		if (rangeLog.location != NSNotFound) {
			readType = FS_LOG_TAG;
			cmd = [[NSString alloc] initWithFormat:@"%@\r\n\r\n", fsCmd];
		} else if (rangeEvent.location != NSNotFound) {
			readType = FS_EVENT_TAG;
			cmd = [[NSString alloc] initWithFormat:@"%@\r\n\r\n", fsCmd];
		} else {
			readType = FS_API_TAG;
			cmd = [[NSString alloc] initWithFormat:@"api %@\r\n\r\n", fsCmd];
		}
		NSLog(cmd);
		NSData *cmdRequest = [cmd dataUsingEncoding:NSASCIIStringEncoding];
		[as writeData:cmdRequest withTimeout:10 tag:readType];
		[as readDataWithTimeout:GENERIC_SOCK_READ_TIMEOUT tag:readType];
		[fsCmd release];
		[cmd release];
		fsCmd = nil;
	}
}

- (BOOL) connectedToNetwork
{
	// Create zero addy
	struct sockaddr_in zeroAddress;
	bzero(&zeroAddress, sizeof(zeroAddress));
	zeroAddress.sin_len = sizeof(zeroAddress);
	zeroAddress.sin_family = AF_INET;
	
	// Recover reachability flags
	SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
	SCNetworkReachabilityFlags flags;
	
	BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
	CFRelease(defaultRouteReachability);
	
	if (!didRetrieveFlags)
	{
		return NO;
	}
	
	BOOL isReachable = flags & kSCNetworkFlagsReachable;
	BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
	return (isReachable && !needsConnection) ? YES : NO;
}

#pragma mark Start/Stop Helpers
-(void) start {
	
	if (![self connectedToNetwork]) {
		UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Network Connection Error" message:@"You need to be connected to the internet to use this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
		[alert show];
		[alert release];
		
		[self writeToConsole:@"Network Error - You need to be connected to the internet to use this feature.\n"];
	} else {	
		consoleTextView.text = @"";
		if (userSetting == nil) {
			[self writeToConsole:@"Error loading user information.\n"];
		} else {
			consoleTextView.font = [UIFont systemFontOfSize: 12.0];
		}
		as = [[AsyncSocket alloc] initWithDelegate:self];
	
		[self handleFSConnection];
	}
}

-(void) stop {
	if (timer != nil) {
		[timer invalidate];
		timer = nil;
	}
	[as release];
	if (fsCmd != nil) {
		[fsCmd release];
		fsCmd = nil;
	}
}

- (void) setUserSetting:(UserSetting *)usrSet {
	userSetting = usrSet;
}

#pragma mark ScrollView Callbacks
- (UIView *) viewForZoomingInScrollView: (UIScrollView *) scrollView
{	
	return consoleTextView;	
}
- (void) scrollViewDidEndZooming: (UIScrollView *) scrollView withView: (UIView *) view atScale: (float) scale
{
	CGAffineTransform transform = CGAffineTransformIdentity;
	transform = CGAffineTransformScale(transform, scale, scale);
	view.transform = transform;
	//[self.nextResponder touchesEnded: touches withEvent:event];
}
/*
- (void)scrollViewDidScroll:(UIScrollView *) scrollView;
{
	CGPoint	p = scrollView.contentOffset;
	consoleTextView.contentOffset = p;
	NSLog(@"x = %f, y = %f", p.x, p.y);
}
*/

#pragma mark UITextField Callbacks
- (BOOL) canBecomeFirstResponder {
	return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
	return YES;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
	[consoleInputField resignFirstResponder];
	if ([consoleInputField.text length] > 0) {
		fsCmd = [[NSMutableString alloc] initWithString: consoleInputField.text];
		[self sendFSCmdData];
	}
	return YES;
}
#define kOFFSET_FOR_KEYBOARD 175.0 //portrait keyboard.
#define kOFFSET_FOR_KEYBOARD2 165.0  //landscape keyboard.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
	UIInterfaceOrientation toOrientation = self.interfaceOrientation;
	[UIView beginAnimations:@"move blocks up" context:nil];
	CGRect rect = self.view.frame;
	if (toOrientation == UIInterfaceOrientationPortrait || toOrientation == UIInterfaceOrientationPortraitUpsideDown) {
		rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
	} else {
		rect.origin.y -= kOFFSET_FOR_KEYBOARD2;
        rect.size.height += kOFFSET_FOR_KEYBOARD2;
	}
	self.view.frame = rect;
	[UIView commitAnimations];
	return YES;
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
	UIInterfaceOrientation toOrientation = self.interfaceOrientation;
	[UIView beginAnimations:@"move blocks down" context:nil];
	CGRect rect = self.view.frame;
	if (toOrientation == UIInterfaceOrientationPortrait || toOrientation == UIInterfaceOrientationPortraitUpsideDown) {
		rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
	} else {
		rect.origin.y += kOFFSET_FOR_KEYBOARD2;
        rect.size.height -= kOFFSET_FOR_KEYBOARD2;
	}
	self.view.frame = rect;
	[UIView commitAnimations];
	return YES;
}

#pragma mark UIView Callbacks
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	self.title = @"FreeSWITCH Console";
	consoleTextView.text = @"";
	consoleTextView.editable = NO;
	consoleInputField.autocorrectionType = UITextAutocorrectionTypeNo;
	consoleInputField.delegate = self;
	consoleTextView.delegate = self;
	
    [super viewDidLoad];
}

- (void) setDelegate:(id) delegate {
	_delegate = delegate;
}

- (id) delegate {
	return _delegate;
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    //return (interfaceOrientation == UIInterfaceOrientationPortrait);
	return YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
	[self stop];
	[consoleTextView release];
	[consoleInputField release];
    [super dealloc];
}

- (void) timerCallback: (NSTimer *)timer {
	NSLog(@"timer fired.\n");
	[as readDataWithTimeout:GENERIC_SOCK_READ_TIMEOUT tag:FS_LOG_TAG];
}

#pragma mark AsyncSocket Callbacks
- (void) onSocket:(AsyncSocket *) sock didConnectToHost:(NSString *) host port:(UInt16) port {
	
}

- (void) onSocket:(AsyncSocket *) sock willDisconnectWithError:(NSError *) error {
	NSString *tmpStr = [[NSString alloc] initWithFormat:@"%@",error];
	[self writeToConsole:tmpStr];
	[tmpStr release];
}

- (void) onSocketDidDisconnect:(AsyncSocket *) sock {
	
}

- (void) onSocket:(AsyncSocket *) sock didWriteDataWithTag:(long) tag {
	//NSLog(@"We've written data. tag=%d\n", tag);
}

//static const NSString *authFlagMarker = @"auth/request";
//static const NSString *authPositiveMarker = @"+OK accepted";
//static const NSString *fsEndResponseMarker = @"\n\n";
- (void) onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag {
	NSString *tmpStr = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding];
	NSLog(@"%@", tmpStr);
	[self writeToConsole:tmpStr];
	if (tag == FS_API_TAG || tag == FS_AUTH_TAG) {
		[as readDataWithTimeout:GENERIC_SOCK_READ_TIMEOUT tag:tag];
	} else if (timer == nil) {
		NSLog(@"starting timer.");
		[self timerCallback:nil];
		timer = [NSTimer timerWithTimeInterval: 120.0 
										target: self 
									  selector: @selector(timerCallback:)
										  userInfo: NULL
										   repeats: TRUE];
		[[NSRunLoop currentRunLoop] addTimer: timer forMode: NSDefaultRunLoopMode];
	}
	[tmpStr release];
}

@end
